/*
 * Copyright (C) 2008 Peter Kling
 * 
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 * 
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with 'A Kind of Billiard'. If not, see
 * <http://www.gnu.org/licenses/>.
 */

package de.akob.particles;

/*
 * Created on Mar 3, 2004
 * 
 * @author Irene Kam (kaminc@cox.net)
 */

import de.akob.mathematics.MathVector;

/**
 * This is a particle Object, which holds all the property of one particle.
 */
public class Particle {
	
	// MagicNumber Definition: Influences the RandomValue returned by the getRandomValue() Method.
	public static final int FIX_GRAVITY          = 0;
	public static final int ONE_SIDE_GRAVITY     = 1;
	public static final int TWO_SIDE_GRAVITY     = 2;
	public static final int SINUS_CURVE_WITH_EXP = 3;
	public static final int SINUS_CURVE          = 4;
	
	// This Particle's life. Full life has the value 1.0f.
	protected double        life;
	// Fade speed (i.e. how fast this particle's life decreases).
	protected double        fade;
	// Red, green, and blue colors for this particle.
	protected double        red;
	protected double        green;
	protected double        blue;
	protected double        grey                 = 0.5f;
	// X, Y, Z locations for this particle.
	protected MathVector    position             = new MathVector(3);
	
	// TODO: Do I need speed? I think in the moment we only use gravity.. X, Y, Z directions and speeds for this
	// particle.
	protected MathVector    speed                = new MathVector(3);
	
	// X, Y, Z gravity on this particle(direction and pull force).
	protected MathVector    gravity              = new MathVector(3);
	
	protected double        xGravity, yGravity, zGravity;
	
	// 0 : Assign Fix Value
	// 1: Assign between 0 and Value
	// 2: Assign between -Value and Value
	protected int[]         randomGravity        = { 0, 0, 0 };
	
	// Slowdown factor: Is given by Particle Engine)
	protected double        slowdown;
	// Size of the particle
	protected double        size;
	// The parent object.
	ParticleEngine          m_oParent;
	
	// WarmUp-Phase
	protected boolean       visible              = false;
	
	/**
	 * Creates a Particle instance with it's properties initialized.
	 */
	public Particle(ParticleEngine parent) {
		m_oParent = parent;
		// Initialize the property values.
		speed = new MathVector(3);
		speed.set(1, 0);
		speed.set(2, 0);
		speed.set(3, 0);
		gravity = new MathVector(3);
		gravity.set(1, 0);
		gravity.set(2, 0);
		gravity.set(3, 0);
		slowdown = m_oParent.getSlowDown();
		size = m_oParent.getParticleSize();
		resetCommonProperties();
		
	}
	
	/**
	 * Retrieves it's blue intensity.
	 * 
	 * @return A double value for this particle's blue intensity.
	 */
	public double getBlue() {
		return blue;
	}
	
	/**
	 * Retrieves this particles' fade speed (how fast the particle dies).
	 * 
	 * @return A double value for this particle's fade speed.
	 */
	public double getFade() {
		return fade;
	}
	
	public MathVector getGravity() {
		return gravity;
	}
	
	/**
	 * Retrieves this particle's green intensity.
	 * 
	 * @return A double value for this particle's green intensity.
	 */
	public double getGreen() {
		return green;
	}
	
	protected double getGreyValue() {
		return grey;
	}
	
	/**
	 * Retrieves this particle's life.
	 * 
	 * @return A double value for this particle's life.
	 */
	public double getLife() {
		return life;
	}
	
	public MathVector getPosition() {
		return position;
	}
	
	/**
	 * Generates a random value for the particle's fade speed.
	 * 
	 * @return A random double value.
	 */
	protected double getRandomFadeValue() {
		return (Math.random() * 100) / 10000f + .0005f;
	}
	
	public int[] getRandomGravity() {
		return randomGravity;
	}
	
	/**
	 * Returns a random double value between 0 inclusive and maxSpeed exclusive.
	 * 
	 * @param magicNumber 1 for random Value between 0 and maxSpeed, 2 for -maxSpeed to maxSpeed
	 * @param maxSpeed
	 * @param time
	 */
	protected double getRandomValue(int magicNumber, double maxSpeed, double time) {
		if (magicNumber == 1)
			return Math.random() * maxSpeed;
		else if (magicNumber == 2)
			return Math.random() * maxSpeed * ((Math.random() >= 0.5) ? 1 : -1);
		else if (magicNumber == 0)
			return maxSpeed;
		else if (magicNumber == 3)
			return Math.sin(time) * Math.exp(-time / 10) * maxSpeed;
		else if (magicNumber == 4)
			return Math.sin(time) * maxSpeed;
		return 1;
	}
	
	/**
	 * Retrieves this particle's red intensity.
	 * 
	 * @return A double value for this particle's red intensity.
	 */
	public double getRed() {
		return red;
	}
	
	public double getSize() {
		return size;
	}
	
	public double getSlowdown() {
		return slowdown;
	}
	
	public MathVector getSpeed() {
		return speed;
	}
	
	public double getXGravity() {
		return xGravity;
	}
	
	public double getYGravity() {
		return yGravity;
	}
	
	public double getZGravity() {
		return zGravity;
	}
	
	/**
	 * Checks to see if this particle is alive. The particle is alive if it's life is >= 0. In the first round, every
	 * particle is dead, because it is initially not visible to get a nicer start, instead of a big blub.
	 * 
	 * @return Returns true if particle is alive. Otherwise returns false.
	 */
	public boolean isAlive() {
		if (life > 0.0f && visible)
			return true;
		else
			return false;
		
	}
	
	/**
	 * Moves the particle, updates Speed and Life
	 */
	public void moveParticle() {
		MathVector slowDown = speed.clone();
		
		// Implement Sinus-Curve for Smoke-Like Movement.
		
		slowDown.add(gravity);
		slowDown.mul(1 / (slowdown * 1000));
		position.add(slowDown);
		
		life -= fade;
		if (life < 0)
			restart();
	}
	
	/**
	 * Resets the life, fade, color, and location of this Particle object. *
	 */
	protected void resetCommonProperties() {
		// Give the particle full life when created.
		life = 1.0f;
		// Give the particle random fade value.
		fade = getRandomFadeValue();
		// Set color values
		red = getGreyValue();
		green = getGreyValue();
		blue = getGreyValue();
		// Initial location for the particle is at 0,0,0.
		position.set(1, 0);
		position.set(2, 0);
		position.set(3, 0);
	}
	
	/**
	 * Restarts this particle. .
	 */
	protected void restart() {
		visible = true;
		resetCommonProperties();
		// Assign direction and speed for this particle.
		speed.set(1, 0);
		speed.set(2, 0);
		speed.set(3, 0);
		// Assign gravity. If Random, then a random value between 0 and Gravity is chosen.
		// Otherwise fixed gravity is assigned
		if (randomGravity[0] > 0)
			gravity.set(1, getRandomValue(randomGravity[0], xGravity, 0));
		else
			gravity.set(1, xGravity);
		if (randomGravity[1] > 0)
			gravity.set(2, getRandomValue(randomGravity[1], yGravity, 0));
		else
			gravity.set(2, yGravity);
		if (randomGravity[2] > 0)
			gravity.set(3, getRandomValue(randomGravity[2], zGravity, 0));
		else
			gravity.set(3, zGravity);
		
		slowdown = m_oParent.getSlowDown();
		size = m_oParent.getParticleSize();
	}
	
	/**
	 * Sets this particle's blue intensity.
	 * 
	 * @param f A double value for this particle's blue intensity.
	 */
	public void setBlue(double f) {
		blue = f;
	}
	
	/**
	 * Sets this particle's fade speed.
	 * 
	 * @param f A double value for the particle's fade speed.
	 */
	public void setFade(double f) {
		fade = f;
	}
	
	public void setGravity(MathVector gravity) {
		this.gravity = gravity;
	}
	
	/**
	 * Sets this particle's green intensity.
	 * 
	 * @param f A double value for this particle's green intensity.
	 */
	public void setGreen(double f) {
		green = f;
	}
	
	/**
	 * Sets this particle's life.
	 * 
	 * @param f A double value for this particle's life.
	 */
	public void setLife(double f) {
		life = f;
	}
	
	public void setPosition(MathVector position) {
		this.position = position;
	}
	
	public void setRandomGravity(int[] randomGravity) {
		this.randomGravity = randomGravity;
	}
	
	/**
	 * Sets this particle's red intensity.
	 * 
	 * @param f A double value for this particle's red intensity.
	 */
	public void setRed(double f) {
		red = f;
	}
	
	public void setSize(double size) {
		this.size = size;
	}
	
	public void setSlowdown(double slowdown) {
		this.slowdown = slowdown;
	}
	
	public void setSpeed(MathVector speed) {
		this.speed = speed;
	}
	
	public void setXGravity(double gravity) {
		xGravity = gravity;
	}
	
	public void setYGravity(double gravity) {
		yGravity = gravity;
	}
	
	public void setZGravity(double gravity) {
		zGravity = gravity;
	}
	
}
